/*
 * Copyright (c)  2015~2020, xforceplus
 * All rights reserved.
 * Project:datacare-orm
 * Id: AbstractSqlManager.java   2020-10-10 22-34-28
 * Author: Evan
 */
package cn.datacare.orm.manager.impl;

import cn.datacare.orm.annotation.id.Id;
import cn.datacare.orm.connection.ConnectionSource;
import cn.datacare.orm.constant.SqlSourceType;
import cn.datacare.orm.conversion.DefaultSqlIdNameConversion;
import cn.datacare.orm.conversion.NameConversion;
import cn.datacare.orm.conversion.SqlIdNameConversion;
import cn.datacare.orm.database.ClassDesc;
import cn.datacare.orm.database.TableDesc;
import cn.datacare.orm.dialect.Dialect;
import cn.datacare.orm.engine.Beetl;
import cn.datacare.orm.interceptor.Interceptor;
import cn.datacare.orm.loader.ClassPathLoader;
import cn.datacare.orm.loader.SQLLoader;
import cn.datacare.orm.manager.Manager;
import cn.datacare.orm.mapper.MapperBuilder;
import cn.datacare.orm.mapper.builder.MapperConfig;
import cn.datacare.orm.mapping.BeanProcessor;
import cn.datacare.orm.source.SQLScript;
import cn.datacare.orm.source.SQLSource;
import cn.datacare.orm.util.kit.StringKit;

import java.util.HashMap;
import java.util.Map;

import static cn.datacare.orm.constant.Constants.PARAM_ROOT;
import static cn.datacare.orm.util.Separator.DOT;

/**
 * <p>
 * Title: 抽像类
 * </p>
 * <p>
 * Description: 抽像类
 * </p>
 * <p>
 * Copyright: 2015~2020
 * </p>
 * <p>
 * Company/Department: xforceplus
 * </p>
 *
 * @author Evan
 * <b>Creation Time:</b> 2020-10-10 22-34-28
 * @since V1.0
 */
public abstract class AbstractSqlManager implements Manager {

    /**
     * DefaultSqlPath:{@value}
     */
    protected static final String DEFAULT_SQL_PATH = "/sql";

    /***
     *  每个sqlManager都有一个标示，可以通过标识来找到对应的sqlManager，用于序列化和反序列化
     */
    protected static Map<String, SQLManager> sqlManagerMap = new HashMap<>();
    /***
     * 拦截器
     */
    protected Interceptor[] inters = {};
    /***
     * 模板引擎
     */
    protected Beetl beetl = null;
    /***
     * 是否从0开始
     *
     */
    protected boolean offsetStartZero = false;
    /**
     * 映射jdbc Result到java对象的工具类，跟sqlId相关
     */
    protected Map<String, BeanProcessor> processors = new HashMap<>();
    /**
     * 默认的映射工具类
     */
    protected BeanProcessor defaultBeanProcessors = null;

    protected Map<String, Id> idAutoGenMap = new HashMap<>();
    /**
     * 数据方言
     */
    protected Dialect dialect;
    /**
     * SQL加载器
     */
    protected SQLLoader sqlLoader;
    /**
     * 数据库连接管理
     */
    protected ConnectionSource connectionSource;
    /**
     * 名字转换器
     */
    protected NameConversion nameConversion;
    /**
     * MateDataManager
     */
    protected MetaDataManager metaDataManager;
    /**
     * 数据库默认的schema，对于单个schema应用，无需指定，但多个schema，需要指定默认的schema
     */

    protected String defaultSchema;
    /**
     * MapperConfig
     */
    protected MapperConfig mapperConfig = new MapperConfig();
    /**
     * sqlManagerName
     */
    protected String sqlManagerName;
    /**
     * 指示sqlManager 处理刷新状态，导致的数据问题可以等待sqlManager重新变为一
     */
    protected int refreshStatus = 0;
    /***
     * 类加载器
     */
    protected ClassLoader entityLoader;

    protected SqlIdNameConversion idNameConversion = new DefaultSqlIdNameConversion();

    public static Map<String, SQLManager> getSqlManagerMap() {
        return sqlManagerMap;
    }

    /**
     * 每个sqlManager都有个名称，如果未指定，默认是dbStyle 返回的名称，即数据库名
     *
     * @param name
     * @return
     */
    public static SQLManager getSQLManagerByName(String name) {
        SQLManager sqlManager = sqlManagerMap.get(name);
        if (sqlManager == null) {
            throw new NullPointerException("不能根据" + name + "获得sqlManager");
        }
        return sqlManager;
    }

    protected MetaDataManager initMMetaDataManager(Manager manager) {
        if (this.metaDataManager == null) {
            this.metaDataManager = new MetaDataManager(this.connectionSource, manager);
        }
        return metaDataManager;
    }

    @Override
    public Interceptor[] getInters() {
        return inters;
    }

    /**
     * 设置Interceptor
     *
     * @param inters
     */
    public void setInters(Interceptor[] inters) {
        this.inters = inters;
    }

    @Override
    public Beetl getBeetl() {
        return beetl;
    }

    public boolean isOffsetStartZero() {
        return offsetStartZero;
    }

    @Override
    public Map<String, BeanProcessor> getProcessors() {
        return processors;
    }

    /**
     * 为指定的sqlId提供一个处理类，可以既可以是一个sqlId，也可以是namespace部分，
     * 所有属于namesapce的都会被此BeanProcessor 处理
     *
     * @param processors
     */
    public void setProcessors(Map<String, BeanProcessor> processors) {
        this.processors = processors;
    }

    @Override
    public BeanProcessor getDefaultBeanProcessors() {
        return defaultBeanProcessors;
    }

    /**
     * 设置默认的jdbc 到 bean的映射处理类，用户可以自己扩展处理最新的类型
     *
     * @param defaultBeanProcessors
     */
    public void setDefaultBeanProcessors(BeanProcessor defaultBeanProcessors) {
        this.defaultBeanProcessors = defaultBeanProcessors;
    }

    public Map<String, Id> getIdAutoGenMap() {
        return idAutoGenMap;
    }

    public Dialect getDialect() {
        return dialect;
    }

    @Override
    public SQLLoader getSqlLoader() {
        return sqlLoader;
    }

    @Override
    public ConnectionSource getConnectionSource() {
        return connectionSource;
    }

    @Override
    public NameConversion getNameConversion() {
        return nameConversion;
    }

    @Override
    public MetaDataManager getMetaDataManager() {
        return metaDataManager;
    }

    @Override
    public String getDefaultSchema() {
        return defaultSchema;
    }

    public MapperConfig getMapperConfig() {
        return mapperConfig;
    }

    public String getSqlManagerName() {
        return sqlManagerName;
    }

    public int getRefreshStatus() {
        return refreshStatus;
    }

    @Override
    public ClassLoader getEntityLoader() {
        return entityLoader;
    }

    /**
     * 设置classloder，如果没有，pojo的初始化使用ContextClassLoader或者加载Beetlsql的classLoader
     *
     * @param entityLoader
     */
    public void setEntityLoader(ClassLoader entityLoader) {
        this.entityLoader = entityLoader;
        if (this.sqlLoader instanceof ClassPathLoader) {
            ((ClassPathLoader) sqlLoader).setClassLoader(entityLoader);
        }
    }

    /**
     * 设置参数
     *
     * @param params
     * @return
     */
    protected Map<String, Object> setParams(Object params) {
        Map<String, Object> map = new HashMap<>(1);
        map.put(PARAM_ROOT, params);
        return map;
    }

    /**
     * @param c 设置一个基接口, 也是推荐的编程方式, 这样可以与框架解耦
     */
    public MapperConfig setBaseMapper(Class c) {
        this.mapperConfig = new MapperConfig(c);
        return this.mapperConfig;
    }

    /**
     * 是否是生产模式:生产模式MetadataManager ，不查看sql文件变化,默认是false
     *
     * @return
     */
    public boolean isProductMode() {
        boolean productMode = !sqlLoader.isAutoCheck();
        return productMode;
    }

    /**
     * 为不存在的表设置一个数据库真正的表，以用于获取metadata，用于数据库分库分表
     *
     * @param virtualTable
     * @param realTable
     */
    public void addVirtualTable(String realTable, String virtualTable) {
        this.metaDataManager.addTableVirtual(realTable, virtualTable);
    }

    /**
     * 设置MapperBuilder，用来生成java的dao代理类，参考getMapper
     *
     * @param mapperBuilder
     */
    public abstract void setMapperBuilder(MapperBuilder mapperBuilder);

    public SqlIdNameConversion getSQLIdNameConversion() {
        return this.idNameConversion;
    }

    /**
     * 设置sqlId到sql文件映射关系
     *
     * @param sqlIdNc
     */
    public void setSQLIdNameConversion(SqlIdNameConversion sqlIdNc) {
        this.idNameConversion = sqlIdNc;
        this.sqlLoader.setSQLIdNameConversion(sqlIdNc);

    }

    /**
     * 清空缓存，用于动态增加修改表情况下可以
     */
    public void refresh() {
        refreshStatus = -1;
        //清空metadata，清空resource，清空模板缓存
        this.metaDataManager.refresh();
        this.sqlLoader.refresh();
        this.beetl.getGroupTemplate().getProgramCache().clearAll();
        this.refreshStatus = 0;
    }

    /**
     * 得到增删改查模板
     *
     * @param clazz         Class
     * @param sqlSourceType SqlSourceType
     * @return SQLScript
     */
    public SQLScript getScript(Class<?> clazz, SqlSourceType sqlSourceType) {
        //slqId 保持与DefaultSQLIdNameConversion同样命名风格
        String className = StringKit.unCapitalize(clazz.getSimpleName());
        String id = className + DOT + sqlSourceType.getClassSql();

        SQLSource tempSource = this.sqlLoader.getSQL(id);
        if (tempSource != null) {
            return new SQLScript(tempSource, this);
        }
        tempSource = this.dialect.getSqlSource(sqlSourceType, clazz);
        tempSource.setId(id);
        sqlLoader.addSQL(id, tempSource);
        return new SQLScript(tempSource, this);
    }

    protected TableDesc getTableDesc(Class<?> clazz){
        String tableName = this.getNameConversion().getTableName(clazz);
        return  this.metaDataManager.getTable(tableName);
    }

    /**
     * 获取ClassDesc
     * @param clazz
     * @return ClassDesc
     */
    protected ClassDesc getClassDesc(Class<?> clazz){
        TableDesc table =this.getTableDesc(clazz);
        return  table.getClassDesc(clazz,this.getNameConversion());
    }

    /**
     * @return
     */
    @Override
    public String getSQLManagerName() {
        return this.sqlManagerName;
    }
}
